#pragma once
#include <zGraphicsConfig.hpp>
#include "../zGraphicsConfig.hpp"
#include "Context.hpp"
#include "../Graphics/Graphics.hpp"
#include "../GraphicsGUI/GraphicsGUI.hpp"
#include "../Renderer/Renderer.hpp"

//GLUT
#define FREEGLUT_STATIC
#define FREEGLUT_LIB_PRAGMAS 0
#include <GL/freeglut.h>

//GLUI
#ifdef ZZZ_CONTEXT_GLUI
#define GLUI_NO_LIB_PRAGMA
#include <GL/glui.h>
#endif

namespace zzz{

class ZGRAPHICS_CLASS GLUTContext : public Context {
public:
  virtual ~GLUTContext();

public:
  typedef enum{NOGLUI,GLUISUBWINDOW,GLUIWINDOW} GluiMode;
  bool Initialize(Renderer* renderer, GluiMode mode=NOGLUI, const char *title=0, int sizex=600, int sizey=600);

  void Redraw() {
    MakeCurrent();
    glutPostRedisplay();
  }
  void SwapBuffer() {
    MakeCurrent();
    glutSwapBuffers();
  }
  void MakeCurrent() {
    glutSetWindow(window_id);
    Context::MakeCurrent();
  }
  void HideCursor() {
    glutSetCursor(GLUT_CURSOR_NONE);
  }
  void ShowCursor() {
    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
  }
  void StartMainLoop() {
    MakeCurrent();
    glutMainLoop();
  }
  int GetWindowID() {
    return window_id;
  }
#ifdef ZZZ_CONTEXT_GLUI
  GLUI *GetGlui() {
    return glui;
  }
#endif
public:
  static void reshape(int nw, int nh) {
    s_context->MakeCurrent();
#ifdef ZZZ_CONTEXT_GLUI
    if (gluimode==GLUISUBWINDOW) {
      int tx,ty,tw,th;
      GLUI_Master.get_viewport_area(&tx,&ty,&tw,&th);
      nw=tw;
      nh=th;
    }
#endif
    s_context->OnSize(0,nw,nh);
  }
  static void display(void) {
    s_context->RenderScene();
  }
  static void mouse(int button, int state, int x, int y) {
    switch(button) {
    case GLUT_LEFT_BUTTON:
      if (state==GLUT_UP) {
        left_=false;
        return s_context->OnLButtonUp(GetInputFlag(),x,y);
      } else if (state==GLUT_DOWN)  {
        left_=true;
        return s_context->OnLButtonDown(GetInputFlag(),x,y);
      }
      break;
    case GLUT_RIGHT_BUTTON:
      if (state==GLUT_UP) {
        right_=false;
        return s_context->OnRButtonUp(GetInputFlag(),x,y);
      } else if (state==GLUT_DOWN) {
        right_=true;
        return s_context->OnRButtonDown(GetInputFlag(),x,y);
      }
      break;
    case GLUT_MIDDLE_BUTTON:
      if (state==GLUT_UP) {
        middle_=false;
        return s_context->OnMButtonUp(GetInputFlag(),x,y);
      } else if (state==GLUT_DOWN) {
        middle_=true;
        return s_context->OnMButtonDown(GetInputFlag(),x,y);
      }
      break;
    }
  }
  static void mousewheel(int button, int dir, int x, int y) {
    if (dir > 0) {
      return s_context->OnMouseWheel(GetInputFlag(),1,x,y);
    } else {
      return s_context->OnMouseWheel(GetInputFlag(),-1,x,y);
    }
  }

  static void motion(int x, int y) {
    return s_context->OnMouseMove(GetInputFlag(),x,y);
  }
  static void passivemotion(int x, int y) {
    return s_context->OnMouseMove(GetInputFlag(),x,y);
  }
  static void key(unsigned char key, int x, int y) {
    s_context->OnKeyDown(key,0,GetInputFlag());
    return s_context->OnChar(key,0,GetInputFlag());
  }
  static void keyup(unsigned char key, int x, int y) {
    s_context->OnKeyUp(key,0,GetInputFlag());
  }
  static void special(int key, int x, int y) {
    return s_context->OnKeyDown(GLUTKey2ZZZKey(key),0,GetInputFlag());
  }
  static void idle() {
    return s_context->OnIdle();
  }
protected:
  static unsigned int GLUTKey2ZZZKey(int key);
  static unsigned int GetInputFlag();
  static GLUTContext *s_context;
  static int window_id;
  static bool left_, right_, middle_;
#ifdef ZZZ_CONTEXT_GLUI
  static GLUI *glui;
#endif
  static GluiMode gluimode;
};
}
